/**
 * @author ahammer	
 * @date   2009 
 * @update don't forget to update development version too!!!!!
 * @update 
 * 
 */

/*
 * This file is part of Iqm.
 * Copyright (c) 2010-2011 Helmut Ahammer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package main;

import java.awt.Dimension;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.util.Properties;
import java.util.Vector;
import javax.media.jai.BorderExtender;
import javax.media.jai.BorderExtenderConstant;
import javax.media.jai.Histogram;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp;
import javax.media.jai.TileCache;
import javax.media.jai.TileScheduler;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.ToolTipManager;
import javax.swing.table.DefaultTableModel;
import org.jfree.ui.RefineryUtilities;
import opgui.IqmOperatorJFrame;
import opjai.IqmAffineDescriptor;
import opjai.IqmAlignDescriptor;
import opjai.IqmBUnwarpJDescriptor;
import opjai.IqmBorderDescriptor;
import opjai.IqmCalcImageDescriptor;
import opjai.IqmCalcValueDescriptor;
import opjai.IqmCoGRegDescriptor;
import opjai.IqmColDeconDescriptor;
import opjai.IqmConvertDescriptor;
import opjai.IqmCreateFracSurfDescriptor;
import opjai.IqmCreateImageDescriptor;
import opjai.IqmCropDescriptor;
import opjai.IqmDFTDescriptor;
import opjai.IqmDirLocalDescriptor;
import opjai.IqmDistMapDescriptor;
import opjai.IqmEdgeDescriptor;
import opjai.IqmFracBoxDescriptor;
import opjai.IqmFracFFTDescriptor;
import opjai.IqmFracHiguchiDescriptor;
import opjai.IqmFracIFSDescriptor;
import opjai.IqmFracMinkowskiDescriptor;
import opjai.IqmFracPyramidDescriptor;
import opjai.IqmHistoModifyDescriptor;
import opjai.IqmImgStabilizerDescriptor;
import opjai.IqmInvertDescriptor;
import opjai.IqmKMeansDescriptor;
import opjai.IqmKMeansFuzzyDescriptor;
import opjai.IqmMorphDescriptor;
import opjai.IqmRGBRelativeDescriptor;
import opjai.IqmROISegmentDescriptor;
import opjai.IqmRankDescriptor;
import opjai.IqmResizeDescriptor;
import opjai.IqmSmoothDescriptor;
import opjai.IqmStatisticsDescriptor;
import opjai.IqmTempMatchDescriptor;
import opjai.IqmThresholdDescriptor;
import opjai.IqmTurboRegDescriptor;
import opjai.IqmUnsharpMaskDescriptor;
import opjai.IqmWatershedDescriptor;
import opjai.IqmWhiteBalanceDescriptor;
import tools.LinearRegression;
import jfreechart.IqmHistoPlot;
import jfreechart.IqmPlot;
import jfreechart.IqmRegressionPlot;
import opgui.preferences.JAIXMLParameterBlocks;

/**
 * This class provides static methods and variables.
 * These methods can be used in every Iqm class or method
 * @author Helmut Ahammer
 */
public class IqmTools {

    //private static File           userDefaultDir = new File(System.getProperty("user.home"));
    private static File userDefaultDir = null;
    private static File currImgPath = new File("./");
    private static int currTankImgIndex = -1;
    private static int maxTankImgIndex = 0;
    private static int currManagerImgIndex = -1;
    private static String iqmVersion;
    private static String javaVersionMin;
    private static String currImgProcessFunc;
    private static ParameterBlockJAI currParameterBlockJAI = null;
    private static boolean virtualFlag;
    private static int numberOfProcessors = 1;
    private static String lineSeparator = null;
    private static String fileSeparator = null;
    private static IqmOperatorJFrame currIqmOperatorGUI = null;
    private static int toolTipReshowDelay = 0;
    private static int toolTipDismissDelay = 0;
    private static int toolTipInitDelay = 0;
    private static TableFrame currTableJFrame = null;
    private static LookDisplayJAI lookDisplayJAI = null;
    private static Vector<Double> currPlotDataX = null;
    private static Vector<Double> currPlotDataY = null;
    private static String currPlotTitleX = null;
    private static String currPlotTitleY = null;
    private static IqmRegressionPlot currRegPlot = null;
    private static JAIXMLParameterBlocks jaitemplatesXML = null; //Global store for all templates @author Philipp W. <java@scaenicus.net>

    //------------------------------------------------------------
    /**
     * This method initializes every IQM image processing operator constructed to be used within the JAI API 
     */
    public static void initJAIUserOperators() {
        IqmInvertDescriptor.register();				 //2009	01
        IqmConvertDescriptor.register();             //2009 03
        IqmResizeDescriptor.register();	             //2009 04
        IqmThresholdDescriptor.register();           //2009 04
        IqmRGBRelativeDescriptor.register();   		 //2009 04
        IqmMorphDescriptor.register();   			 //2009 04
        IqmCalcImageDescriptor.register();			 //2009 05
        IqmCalcValueDescriptor.register();			 //2009 05
        IqmColDeconDescriptor.register();			 //2009 05
        IqmKMeansDescriptor.register();				 //2009 05
        IqmKMeansFuzzyDescriptor.register();		 //2009 05
        IqmTempMatchDescriptor.register();		     //2009 05
        IqmBUnwarpJDescriptor.register();			 //2009 05
        IqmTurboRegDescriptor.register();			 //2009 05
        IqmRankDescriptor.register();			     //2009 06
        IqmHistoModifyDescriptor.register();	     //2009 06
        IqmEdgeDescriptor.register();	    		 //2009 06
        IqmSmoothDescriptor.register();	    		 //2009 06
        IqmUnsharpMaskDescriptor.register();	   	 //2009 06
        IqmAlignDescriptor.register();	   			 //2009 06
        IqmAffineDescriptor.register();	   			 //2009 06
        IqmCropDescriptor.register();	   			 //2009 06
        IqmBorderDescriptor.register();	   			 //2009 06
        IqmStatisticsDescriptor.register();	   		 //2009 06
        IqmFracPyramidDescriptor.register();	     //2009 07
        IqmCoGRegDescriptor.register();				 //2009 07
        IqmFracMinkowskiDescriptor.register();		 //2009 07
        IqmFracIFSDescriptor.register();	    	 //2009 10
        IqmFracBoxDescriptor.register();	         //2009 11
        IqmDFTDescriptor.register();	        	 //2009 11
        IqmWhiteBalanceDescriptor.register();	     //2009 12
        IqmROISegmentDescriptor.register();	         //2009 12
        IqmCreateFracSurfDescriptor.register();	     //2010 01
        IqmCreateImageDescriptor.register();	     //2010 02
        IqmFracHiguchiDescriptor.register();	     //2010 04
        //IqmGradientPathDescriptor.register();	     //2010 04 not implemented yet 2010 09 29
        IqmWatershedDescriptor.register();	         //2010 05
        IqmDirLocalDescriptor.register();	         //2010 05
        IqmImgStabilizerDescriptor.register();		 //2010 05
        IqmDistMapDescriptor.register();			 //2010 05
        IqmFracFFTDescriptor.register();			 //2011 02
    }
//------------------------------------------------------------	

    /**
     *
     * This method deletes all files in the user temp directory
     * except the file iqmDefaultPath.txt  *
     */
    public static void deleteUserTempFiles() {
        String userDirName = System.getProperty("user.home") + "/Iqm/IqmTemp";
        File userDir = new File(userDirName);
        //File userDir = IqmTools.getUserDefaultDir();
        if (userDir.isDirectory()) {
            File[] files = userDir.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isFile()) {
                    if (!files[i].toString().contains("iqm.ini")) {
                        files[i].delete();
                    }
                }
            }
        }
    }
    //------------------------------------------------------------

    /**
     *
     * This method deletes all directories in the user temp directory
     * except
     */
    public static void deleteUserTempDirs() {
        String userDirName = System.getProperty("user.home") + "/Iqm/IqmTemp";
        File userDir = new File(userDirName);
        //File userDir = IqmTools.getUserDefaultDir();
        if (userDir.isDirectory()) {
            File[] files = userDir.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    deleteDir(files[i]);
                }
            }
        }
    }
    //------------------------------------------------------------

    /**
     * This method deletes a directory all files and subdirectories
     * Returns true if all deletions are successful.
     *  If a deletion fails, the method stops to delete and returns false.
     * @throws Exception
     */
    public static boolean deleteDir(File dir) {
        if (dir == null) {
            System.out.println("Iqmtools.deletedir:   no directory defined");
            return false;
        }
        //System.out.println("IqmTools.deleteDir: dir " + dir.toString()) ;
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            for (int i = 0; i < files.length; i++) {
                IqmTools.deleteDir(files[i]);
            }
        }
        boolean success = false;

        if (dir.exists()) {
            success = dir.delete();  // Delete the empty directory
        } else {
            success = false;
        }
        return success;
    }
    //------------------------------------------------------------

    /**
     * This method gets a new temporary directory (name)
     * @return file a new image stack directory in the user temporary directory
     */
    public static File getNewVirtDirectory() {
        String userDirName = System.getProperty("user.home") + "/Iqm/IqmTemp";
        File Dir = new File(userDirName);
        File newDir = null;
        if (Dir.isDirectory()) {
            File[] files = Dir.listFiles();
            int n = 0; //DirectoryFile Nummern
            boolean found = true;
            while (found == true) {
                //if (children[i].isFile && children[i].canRead()) //eventuell auch so
                found = false;
                for (int i = 0; i < files.length; i++) {
                    if (files[i].isDirectory()) {
                        if (files[i].toString().contains("Stack_" + String.format("%04d", n))) {
                            found = true;
                        }
                    }
                }
                n = n + 1;
            }
            n = n - 1; //not found
            newDir = new File(Dir.toString() + "/Stack_" + String.format("%04d", n));
        }
        return newDir;
    }
//------------------------------------------------------------

    /**
     * This method gets a new temporary file (name)
     * The next number is automatically searched and created
     * @return file a new image slice in a  directory
     */
    public static File getNewVirtSlice(File Dir) {
        //String userDirName = System.getProperty("user.home") +"/iqm/iqmTemp";
        //File userDir = new File(userDirName);
        File newSlice = null;
        if (Dir.isDirectory()) {
            File[] files = Dir.listFiles();
            int n = 1; //Beginn StackFile Nummern
            boolean found = true;
            while (found == true) {
                //if (children[i].isFile && children[i].canRead()) //eventuell auch so
                found = false;
                for (int i = 0; i < files.length; i++) {
                    if (files[i].toString().contains("Slice_" + String.format("%04d", n) + ".tif")) {
                        found = true;
                    }
                }
                n = n + 1;
            }
            n = n - 1; //not found
            newSlice = new File(Dir.toString() + "/Slice_" + String.format("%04d", n) + ".tif");
        }
        return newSlice;
    }
    //------------------------------------------------------------

    /**
     * This method gets a new temporary file (name) with number n
     * @return file a new image slice in a  directory
     */
    public static File getNewVirtSlice(File Dir, int n) {
        File newSlice = null;
        if (Dir.isDirectory()) {
            newSlice = new File(Dir.toString() + "/Slice_" + String.format("%04d", n) + ".tif");
        }
        return newSlice;
    }
    //------------------------------------------------------------

    /**
     * this static method returns an image icon
     * @param arg  PlanarImage
     * @return ImageIcon
     */
    public static ImageIcon createTankThumb(PlanarImage pi) {
        return IqmTools.createThumb(pi, 80);
    }
    //------------------------------------------------------------

    /**
     * this static method returns an image icon
     * @param arg  PlanarImage
     * @return ImageIcon
     */
    public static ImageIcon createManagerThumb(PlanarImage pi) {
        return IqmTools.createThumb(pi, 60);
    }

    //------------------------------------------------------------
    /**
     * this static method returns an image icon
     * @param arg  PlanarImage
     * @return ImageIcon
     */
    public static ImageIcon createThumb(PlanarImage arg, int size) {
        ImageIcon ic = null;

        if ((arg instanceof PlanarImage) && (arg != null)) {
            //if (arg != null){
            //int length = 98;
            int height = arg.getHeight();
            int width = arg.getWidth();
            boolean isPortrait = (height > width);
            float zoom = size / (float) (isPortrait ? height : width);

            PlanarImage pi;
            ParameterBlock pb = new ParameterBlock();
            pb.addSource(arg);

            //if (zoom  < 1.0f){
            if ((zoom < 1.0f) && (zoom > 1.0f)) { //damit immer else
                pb.add((double) zoom);//x downsample factor
                pb.add((double) zoom);//y downsample factor
                RenderingHints rh = new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_COPY));
                //rh = null;
                pi = JAI.create("subsampleaverage", pb, rh);  //geht nicht bei manchen Bildern
            } else {
                pb.add(zoom);//x scale factor
                pb.add(zoom);//y scale factor
                pb.add(0.0F);//x translate
                pb.add(0.0F);//y translate
                //pb.add(new InterpolationNearest());//interpolation method
                //pb.add(Interpolation.getInstance(Interpolation.INTERP_BILINEAR)); // errors for 16 bit images
                pb.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST));
                RenderingHints rh = new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_COPY));
                pi = JAI.create("scale", pb, rh);
            }

            //ParameterBlock pb1 = new ParameterBlock();
            pb.removeSources();
            pb.removeParameters();
            pb.addSource(pi);
            pb.add((size - pi.getWidth()) / 2);  //left
            pb.add((size - pi.getWidth()) / 2); //right
            pb.add((size - pi.getHeight()) / 2); //top
            pb.add((size - pi.getHeight()) / 2); //bottom
            double borderColor1[] = {150, 150, 150, 150};   //Farbe au�erhalb thumb
            pb.add(new BorderExtenderConstant(borderColor1));//type  //fill mit zero
            pb.add(borderColor1);//fill color
            pi = JAI.create("border", pb);

            //ParameterBlock pb2 = new ParameterBlock();  //schwarzer Rand
            pb.removeSources();
            pb.removeParameters();
            pb.addSource(pi);
            pb.add(1);  //left
            pb.add(1); //right
            pb.add(1); //top
            pb.add(1); //bottom
            double borderColor2[] = {0, 0, 0, 0};
            pb.add(new BorderExtenderConstant(borderColor2));//type  //fill mit zero
            pb.add(borderColor2);//fill color
            pi = JAI.create("border", pb);

            //RGB Eliminate alpha channel, doesn't work for grey images
//			pb.removeSources();
//			pb.removeParameters();
//			pb.addSource(pi);
//			pb.add(new int[] {0,1,2});
//			pi = JAI.create("bandSelect", pb);

//			pb.removeSources();
//			pb.removeParameters();
//			pb.addSource(pi);  
//			pb.add(DataBuffer.TYPE_BYTE);  
//			pi = JAI.create("format", pb);  

            //pb = new ParameterBlock()
            //pb.addSource(pi);
            //pb.add(sampleModel);
            //ColorModel colorModel = ColorModel.getRGBdefault();
//			ComponentColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int[]{8,8,8}, false, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
//			ImageLayout il = new ImageLayout();
//			il.setSampleModel(colorModel.createCompatibleSampleModel(pi.getWidth(), pi.getHeight()));
//			RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, il);
//			pb = new ParameterBlock();
//			pb.addSource(pi);
//			pb.add(colorModel);
//			il = new ImageLayout();
//			il.setSampleModel(colorModel.createCompatibleSampleModel(pi.getWidth(), pi.getHeight()));
//			hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, il);
//			pi = JAI.create("ColorConvert", pb, hints);
            //pi = JAI.create("colorconvert", pi, ColorSpace.getInstance(ColorSpace.CS_sRGB));
//			System.out.println("IqmTools: pi.getSampleModel().getNumBands()     " + pi.getSampleModel().getNumBands());
//			System.out.println("IqmTools: pi.getSampleModel().getTransferType() " + pi.getSampleModel().getTransferType());
//			System.out.println("IqmTools: pi.getSampleModel().getSampleSize()   " + pi.getSampleModel().getSampleSize());
//			System.out.println("IqmTools: pi.getSampleModel().getDataType()     " + pi.getSampleModel().getDataType());
//			System.out.println("IqmTools: pi.getColorModel().getNumColorComponents() " + pi.getColorModel().getNumColorComponents());
//			System.out.println("IqmTools: pi.getColorModel()..getTransferType()      " + pi.getColorModel().getTransferType());
//			System.out.println("IqmTools: pi.getColorModel().hasAlpha()              " + pi.getColorModel().hasAlpha());
//			


            try {
                BufferedImage bi = pi.getAsBufferedImage();

                //ColorModel cm = PlanarImage.createColorModel( pi.getSampleModel() );
                //BufferedImage bi = pi.getAsBufferedImage(null, cm);

                ic = new ImageIcon(bi);
            } catch (Exception e) {
                System.out.println("IqmTools.createTankThumb(PlanarImage arg): Couldn't make icon");
                //e.printStackTrace();
                PlanarImage piLogo = JAI.create("url", ClassLoader.getSystemResource("imgs/LogoIqm_v1.0_104x78_LightGrey.png"));
                ic = new ImageIcon(piLogo.getAsBufferedImage());
            }
        }
        return ic;
    }

    /**
     * This method checks if a class exists
     * @param arg String of class name
     * @return boolean
     */
//------------------------------------------------------------
    public static Boolean checkIfExists(String arg) {
        FileLock lck = null;
        Boolean out = false;
        try {
            lck = new FileOutputStream("./" + arg + ".lock").getChannel().tryLock();
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
            System.exit(3);
        } catch (IOException ioe) {
            ioe.printStackTrace();
            System.exit(2);
        }
        //if (lck == null){
        //        JOptionPane.showMessageDialog (null, ""+arg+" is already running!", "Information", JOptionPane.INFORMATION_MESSAGE);
        //        System.err.println (""+arg+" is locked!");
        //        System.exit (1);
        //}
        if (lck == null) {
            out = true;
        }
        return out;
    }

//------------------------------------------------------------
    /**
     * This method sets the version of Iqm
     * @param version of Iqm
     */
    public static void setIqmVersion(String arg) {
        iqmVersion = arg;
    }

    /**
     * This method gets the version of Iqm
     * @return version of Iqm
     */
    public static String getIqmVersion() {
        return iqmVersion;
    }
//------------------------------------------------------------

    /**
     * This method sets the version of Java
     * @param version of Java
     */
    public static void setJavaVersionMin(String arg) {
        javaVersionMin = arg;
    }

    /**
     * This method gets the version of Java
     * @return version of Java
     */
    public static String getJavaVersionMin() {
        return javaVersionMin;
    }
//------------------------------------------------------------

    /**
     * This method sets the default directory (image path)
     * @param default directory
     */
    public static void setCurrImgPath(File arg) {
        currImgPath = arg;
    }

    /**
     * This method gets the default directory (image path)
     * @return default directory
     */
    public static File getCurrImgPath() {
        return currImgPath;
    }
//------------------------------------------------------------

    /**
     * This method does nothing
     * This method sets the default user directory
     * @param default user directory
     */
    public static void setUserDefaultDir(File arg) {
        //userDefaultDir = arg;
    }

    /**
     * This method does nothing
     * This method gets the default user directory (image path)
     * @return default user directory
     */
    public static File geUserDefaultDir() {
        //return userDefaultDir;
        return null;
    }
//------------------------------------------------------------

    /**
     * This method sets the current tank image number
     * @param current tank image number
     */
    public static void setCurrTankIndex(int arg) {
        currTankImgIndex = arg;
    }

    /**
     * This method gets the current tank image number
     * @return current tank image number
     */
    public static int getCurrTankIndex() {
        return currTankImgIndex;
    }
//------------------------------------------------------------

    /**
     * This method sets the maximal tank image number
     * @param maximal tank image number
     */
    public static void setMaxTankIndex(int arg) {
        maxTankImgIndex = arg;
    }

    /**
     * This method gets the maximal tank image number
     * @return maximal tank image number
     */
    public static int getMaxTankIndex() {
        return maxTankImgIndex;
    }
//------------------------------------------------------------

    /**
     * This method sets the current manager image number
     * @param current manager image number number
     */
    public static void setCurrManagerImgIndex(int arg) {
        currManagerImgIndex = arg;
    }

    /**
     * This method gets the current manager image number
     * @return current manager image number number
     */
    public static int getCurrManagerImgIndex() {
        return currManagerImgIndex;
    }
//------------------------------------------------------------

    /**
     * This method sets the current image processing function
     * @param String current image processing function
     */
    public static void setCurrImgProcessFunc(String currFunc) {
        currImgProcessFunc = currFunc;
        if ((currFunc == "IqmBUnwarpJ") || (currFunc == "IqmTurboReg") || (currFunc == "IqmCoGReg") || (currFunc == "IqmImgStabilizer")) {
            System.out.println("IqmTools: Parallel processing not allowed");
            ManagerFrame.jCheckBoxSerial.setSelected(true);
            ManagerFrame.jCheckBoxExecuter.setEnabled(false);
            ManagerFrame.jCheckBoxPJ.setEnabled(false);
        } else {
            ManagerFrame.jCheckBoxSerial.setEnabled(true);
            ManagerFrame.jCheckBoxExecuter.setEnabled(true);
            ManagerFrame.jCheckBoxPJ.setEnabled(true);
        }
    }

    /**
     * This method gets the current image processing function
     * @return String current image processing function
     */
    public static String getCurrImgProcessFunc() {
        return currImgProcessFunc;
    }
//------------------------------------------------------------

    /**
     * This method sets the current parameter block for the current image processing function
     * @param ParameterBlock current parameter block
     */
    public static void setCurrParameterBlockJAI(ParameterBlockJAI arg) {
        currParameterBlockJAI = arg;
        //if (arg != null) System.out.println("IqmTools: CurrParamBlock set to "+arg.getIntParameter(0));
    }

    /**
     * This method gets the current parameter block for the current image processing function
     * @return ParameterBlock current parameter clock
     */
    public static ParameterBlockJAI getCurrParameterBlockJAI() {
        return currParameterBlockJAI;
    }
//------------------------------------------------------------

    /**
     * This method sets the virtual modus flag
     * @param boolean
     */
    public static void setVirtualFlag(boolean selected) {
        virtualFlag = selected;
    }

    /**
     * This method gets the satus of the virtual modus flag
     * @return boolean status of virtual flag
     */
    public static boolean isVirtualFlagSet() {
        return virtualFlag;
    }
    //------------------------------------------------------------

    /**
     * This method gets the number of processors
     * @return int number of available processors
     */
    public static int getNumberOfProcessors() {
        numberOfProcessors = Runtime.getRuntime().availableProcessors();
        return numberOfProcessors;
    }
    //------------------------------------------------------------

    /**
     * This methods converts an integer to a string with leading zeros
     * @param int n Number to be converted, int d digits
     */
    public static String getStringWithLeadingZeros(int n, int d) {
        String str = String.valueOf(n);
        while (str.length() < d) {
            str = "0" + str;
        }
        return str;
    }
    //------------------------------------------------------------

    /**
     * This methods converts an integer to a string with leading space
     * It isn't sure that this method works correctly!!!!!
     * It seems that leading space is cropped.
     * @param int n Number to be converted, int d digits
     */
    public static String getStringWithLeadingSpace(int n, int d) {
        String str = String.valueOf(n);
        while (str.length() < d) {
            str = " " + str;
        }
        return str;
    }
    //------------------------------------------------------------

    /**
     * This methods gets the platform specific line separator
     */
    public static String getLineSeparator() {
        lineSeparator = System.getProperty("line.separator");
        return lineSeparator;
    }
    //------------------------------------------------------------

    /**
     * This methods gets the platform specific file separator
     */
    public static String getFileSeparator() {
        fileSeparator = System.getProperty("file.separator");
        return fileSeparator;
    }
    //------------------------------------------------------------

    /**
     * This static method gets the image type
     * @param int Pixelsize int NumBand
     * @return String Image Type
     */
    public static String getImgTyp(int pixelSize, int numBands) {
        String type = "Unknown image type";
        if ((pixelSize == 24) && (numBands == 3)) {
            type = "RGB";
        }
        if ((pixelSize == 8) && (numBands == 1)) {
            type = "8 bit";
        }
        if ((pixelSize == 16) && (numBands == 1)) {
            type = "16 bit";
        }
        return type;
    }
    //------------------------------------------------------------

    /**
     * This static method gets the image type
     * @param SampleModel
     * @return String Image Type
     */
    public static String getImgTyp(PlanarImage pi) {
        String type = "Type?";
        SampleModel sm = pi.getSampleModel();
        ColorModel cm = pi.getColorModel();
        if (cm instanceof IndexColorModel) {
            type = "Palette";
            return type;
        }
        if ((sm.getDataType() == DataBuffer.TYPE_BYTE) && (sm.getNumBands() == 3)) {
            type = "RGB";
        }
        if ((sm.getDataType() == DataBuffer.TYPE_BYTE) && (sm.getNumBands() == 1)) {
            type = "8 bit";
        }
        if ((sm.getDataType() == DataBuffer.TYPE_USHORT) && (sm.getNumBands() == 1)) {
            type = "16 bit";
        }
        if (sm.getDataType() == DataBuffer.TYPE_FLOAT) {
            type = "Float";
        }
        if (sm.getDataType() == DataBuffer.TYPE_DOUBLE) {
            type = "Double";
        }
        if (sm.getDataType() == DataBuffer.TYPE_INT) {
            type = "Integer";
        }
        if (sm.getDataType() == DataBuffer.TYPE_UNDEFINED) {
            type = "Undefined";
        }
        return type;
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method sets current operator GUI
     * @param IqmOperatorGUI op 
     */
    public static void setCurrIqmOperatorGUI(IqmOperatorJFrame gui) {
        currIqmOperatorGUI = gui;

    }

    /**
     * This method gets current operator window
     * @return Object IqmOperatorGUI 
     */
    public static IqmOperatorJFrame getCurrIqmOperatorGUI() {
        return currIqmOperatorGUI;

    }
    //--------------------------------------------------------------------------------------

    /**
     * This method sets current JTable JFrame
     * @param TableFrame fr 
     */
    public static void setCurrTableJFrame(TableFrame fr) {
        currTableJFrame = fr;

    }

    /**
     * This method gets current JTable JFrame
     * @return TableJFrame 
     */
    public static TableFrame getCurrTableJFrame() {
        return currTableJFrame;

    }
    //--------------------------------------------------------------------------------------

    /**
     * This method displays the Table data
     * @param JTable table
     */
    public static void displayTableData(JTable table) {

        TableFrame tableJFrame = null;
        tableJFrame = IqmTools.getCurrTableJFrame();
        if (tableJFrame == null) {
            tableJFrame = new TableFrame();
            tableJFrame.createAndShowGUI();
        }
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        tableJFrame.jTable = table;
        JViewport vp = tableJFrame.jScrollPane.getViewport();
        vp.removeAll();
        vp.add(table);
        //table.setFillsViewportHeight(true);
        IqmTools.setCurrTableJFrame(tableJFrame);
        tableJFrame.toFront();
    }
    //--------------------------------------------------------------------------------------

    public static void displayJoinedJTables(Vector<Object> resultVector) {
        // Objects must be JTables
        int numTables = resultVector.size();
        JTable jtable = (JTable) resultVector.get(0);
        DefaultTableModel model = (DefaultTableModel) jtable.getModel();
        int numColumns = model.getColumnCount();
        Object[] newDummyValues = new Object[numColumns];
        for (int n = 1; n < numTables; n++) { //JTables loop
            int numRows = model.getRowCount();
            DefaultTableModel newModel = (DefaultTableModel) ((JTable) resultVector.get(n)).getModel();
            int numNewRows = newModel.getRowCount();
            for (int r = 0; r < numNewRows; r++) {// Row loop
                model.addRow(newDummyValues);
                for (int c = 0; c < numColumns; c++) { //Columns loop
                    Object newValue = newModel.getValueAt(r, c);
                    model.setValueAt(newValue, r + numRows, c);
                }
            }
        }
        IqmTools.displayTableData(jtable);
    }

    //--------------------------------------------------------------------------------------
    /**
     * This method converts a JTable to the csv format
     * @param  table JTable
     * @return text data String
     */
    public static String convertJTableToCSV(JTable table) {
        String text = "  Iqm Data" + "\n";
        text = text + " \n";
        for (int c = 0; c < table.getModel().getColumnCount(); c++) { //Column Names
            String colName = String.valueOf(table.getModel().getColumnName(c));
            if (colName == null) {
                colName = "";
            }
            colName = colName.replace(",", "");
            text = text + colName;
            if (c < (table.getModel().getColumnCount() - 1)) {
                text = text + ",";
            }
            if (c == (table.getModel().getColumnCount() - 1)) {
                text = text + "\n";
            }
        }

        for (int i = 0; i < table.getModel().getRowCount(); i++) { //Data rows
            for (int j = 0; j < table.getModel().getColumnCount(); j++) {
                int col = table.convertColumnIndexToView(j);
                String currVal = String.valueOf(table.getModel().getValueAt(i, col));
                if (currVal == null) {
                    currVal = "";
                }
                currVal = currVal.replace(",", "");
                text = text + currVal;
                if (j < (table.getModel().getColumnCount() - 1)) {
                    text = text + ",";
                }
                if (j == (table.getModel().getColumnCount() - 1)) {
                    text = text + "\n";
                }

            }
        }
        return text;

    }
    //--------------------------------------------------------------------------------------

    /**
     * This method converts a JTable to the tab delimited format
     * @param  table JTable
     * @return text data String
     */
    public static String convertJTableToTabDelimeted(JTable table) {
        String text = "  Iqm Data" + "\n";
        text = text + " \n";
        for (int c = 0; c < table.getModel().getColumnCount(); c++) { //Column Names
            String colName = String.valueOf(table.getModel().getColumnName(c));
            if (colName == null) {
                colName = "";
            }
            //colName = colName.replace(",", "");
            text = text + colName;
            if (c < (table.getModel().getColumnCount() - 1)) {
                text = text + "\t";  //Tabulator
            }
            if (c == (table.getModel().getColumnCount() - 1)) {
                text = text + "\n";
            }
        }

        for (int i = 0; i < table.getModel().getRowCount(); i++) { //Data rows
            for (int j = 0; j < table.getModel().getColumnCount(); j++) {
                int col = table.convertColumnIndexToView(j);
                String currVal = String.valueOf(table.getModel().getValueAt(i, col));
                if (currVal == null) {
                    currVal = "";
                }
                //currVal = currVal.replace(",", "");
                text = text + currVal;
                if (j < (table.getModel().getColumnCount() - 1)) {
                    text = text + "\t";
                }
                if (j == (table.getModel().getColumnCount() - 1)) {
                    text = text + "\n";
                }

            }
        }
        return text;

    }
    //--------------------------------------------------------------------------------------

    /**
     * This method sets the default toolTipDelays
     * It is called at startup
     */
    public static void setDefaultToolTipDelays() {
        ToolTipManager ttm = ToolTipManager.sharedInstance();
        toolTipInitDelay = ttm.getInitialDelay();
        toolTipReshowDelay = ttm.getReshowDelay();
        toolTipDismissDelay = ttm.getDismissDelay();
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method gets the default toolTipInitDelay
     * It is called e.g after jFreeChart
     * @return int toolTipInitDelay
     */
    public static int getDefaultToolTipInitDelay() {
        return toolTipInitDelay;
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method gets the default toolTipReshowDelay
     * It is called e.g after jFreeChart
     * @return int toolTipReshowDelay
     */
    public static int getDefaultToolTipReshowDelay() {
        return toolTipReshowDelay;
    }

    //--------------------------------------------------------------------------------------
    /**
     * This method gets the default toolTipDismissDelay
     * It is called e.g after jFreeChart
     * @return int toolTipDismissDelay
     */
    public static int getDefaultToolTipDismissDelay() {
        return toolTipDismissDelay;
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method sets the default JAI Tile calculation properties
     */
    public static void setJAIDefaultOptions() {
        final long memCapacity = 1024L * 1024 * 1024; // 1024MByte
        //final long memCapacity = 0L; // 256MByte
        final long tileCapacity = 1000L;
        JAI.getDefaultInstance().setTileCache(JAI.createTileCache(memCapacity));
        JAI.getDefaultInstance().setRenderingHint(JAI.KEY_CACHED_TILE_RECYCLING_ENABLED, Boolean.FALSE);
        TileScheduler ts = JAI.createTileScheduler();
        ts.setPriority(Thread.MAX_PRIORITY);
        //ts.setParallelism(2);  //default 2
        JAI.getDefaultInstance().setTileScheduler(ts);
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method gets default Tile Cache Rendering Hints
     * used for user operators, such as IqmInvert, IqmThreshold, IqmRGBRelative,...
     */
    public static RenderingHints getDefaultTileCacheRenderingHints() {
        final long tileCacheSize = 256 * 1024 * 1024L; // 256 Megabytes;
        //
        //	  //DEFAULT_RENDERING_HINTS.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        //	  //RenderingHints defaultRendHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        //	  //defaultRendHints.put(JAI.KEY_INTERPOLATION, Interpolation.getInstance(Interpolation.INTERP_BICUBIC));
        //	  // .... more hints set to the default values
        //
        //	  //RenderingHints rendHints = (RenderingHints)defaultRendHints.clone();
        //	  //rendHints.put(JAI.KEY_TILE_CACHE, tileCache);
        //
        TileCache tc = JAI.createTileCache();
        tc.setMemoryCapacity(tileCacheSize);
        RenderingHints rh = new RenderingHints(JAI.KEY_TILE_CACHE, tc);
        return rh;
        //((TileCache) rh.get(JAI.KEY_TILE_CACHE)).flush();
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method sets the current plot data
     * @param dataX
     * @param dataY
     * @param xTitle
     * @param yTitle
     */
    public static void setCurrPlotXYData(Vector<Double> dataX, Vector<Double> dataY, String titleX, String titleY) {
        currPlotDataX = dataX;
        currPlotDataY = dataY;
        currPlotTitleX = titleX;
        currPlotTitleY = titleY;
    }

    /**
     * This method gets current plot data
     * @return Vector
     */
    public static Vector<Double> getCurrPlotDataX() {
        return currPlotDataX;
    }

    /**
     * This method gets current plot data
     * @return Vector
     */
    public static Vector<Double> getCurrPlotDataY() {
        return currPlotDataY;
    }

    /**
     * This method gets current plot data
     * @return String
     */
    public static String getCurrPlotTitleX() {
        return currPlotTitleX;
    }

    /**
     * This method gets current plot data
     * @return String
     */
    public static String getCurrPlotTitleY() {
        return currPlotTitleY;
    }

    /**
     * This method plots the histogram in an separate window
     */
    public static void plotHistogram(PlanarImage pi) {
        //HistogramPanel hp = new HistogramPanel();
        //SimpleGraph sg = new SimpleGraph("X v. Y Demo", "x", "y");
        double typeGreyMax = IqmTools.getImgTypeGreyMax(pi);
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(pi);
        pb.add(null); //Roi
        pb.add(1); //Sampling
        pb.add(1);
        pb.add(new int[]{(int) typeGreyMax + 1}); //Number of bins
        pb.add(new double[]{0d}); //Start
        pb.add(new double[]{typeGreyMax + 1}); //End
        RenderedOp rendOp = JAI.create("histogram", pb);
        Histogram histo = (Histogram) rendOp.getProperty("histogram");
        String frameTitle = " ";
        try {
            frameTitle = (String) rendOp.getProperty("file_name");
        } catch (Exception e1) {
            //do nothing
        }
        String histoTitle = (String) rendOp.getProperty("image_name");
        //jFreeChart
        IqmHistoPlot hp = new IqmHistoPlot(histo, frameTitle, histoTitle);
        hp.pack();
        //int horizontalPercent = 5;
        //int verticalPercent = 5;
        //RefineryUtilities.positionFrameOnScreen(hp, horizontalPercent, verticalPercent);
        RefineryUtilities.centerFrameOnScreen(hp);
        hp.setVisible(true);
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method plots XY Values in an separate window without the x data
     */
    public static void plotXY(Vector dataY, boolean isLineVisible, String xTitle, String yTitle) {
        int length = dataY.size();
        Vector dataX = new Vector();
        for (int i = 0; i < length; i++) {
            dataX.add((double) (i + 1));
        }
        IqmTools.plotXY(dataX, dataY, isLineVisible, xTitle, yTitle);
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method plots XY Values in an separate window
     */
    public static void plotXY(Vector<Double> dataX, Vector<Double> dataY, boolean isLineVisible, String xTitle, String yTitle) {

        //SimpleGraph sg = new SimpleGraph("X v. Y Demo", "x", "y");
        PlanarImage pi = Tank.getCurrentTankImageAt(IqmTools.getCurrManagerImgIndex());

        String frameTitle = " ";
        String plotTitle = " ";
        try {
            frameTitle = (String) pi.getProperty("file_name");
        } catch (Exception e1) {
            //do nothing
        }
        plotTitle = (String) pi.getProperty("image_name");
        //jFreeChart
        IqmPlot pl = new IqmPlot(dataX, dataY, isLineVisible, frameTitle, plotTitle, xTitle, yTitle);
        pl.pack();
        //int horizontalPercent = 5;
        //int verticalPercent = 5;
        //RefineryUtilities.positionFrameOnScreen(pl, horizontalPercent, verticalPercent);
        RefineryUtilities.centerFrameOnScreen(pl);
        pl.setVisible(true);
    }
    //--------------------------------------------------------------------------------------

    /**
     * This method plots XY Values in an separate window
     */
    public static void displayRegressionPlotXY(Vector dataX, Vector dataY, boolean isLineVisible,
            String frameTitle, String plotTitle, String xTitle, String yTitle,
            int regStart, int regEnd, boolean deleteExistingPlot) {
        if (deleteExistingPlot) {
            IqmRegressionPlot plCurr = IqmTools.getCurrRegPlot();
            if (plCurr != null) {
                plCurr.setVisible(false);
                plCurr.dispose();
            }

        }
        //SimpleGraph sg = new SimpleGraph("X v. Y Demo", "x", "y");

        //jFreeChart
        IqmRegressionPlot pl = new IqmRegressionPlot(dataX, dataY, isLineVisible, frameTitle, plotTitle, xTitle, yTitle, regStart, regEnd);
        pl.pack();
        //int horizontalPercent = 5;
        //int verticalPercent = 5;
        //RefineryUtilities.positionFrameOnScreen(pl, horizontalPercent, verticalPercent);
        RefineryUtilities.centerFrameOnScreen(pl);
        pl.setVisible(true);
        IqmTools.setCurrRegPlot(pl);

    }
    //----------------------------------------------------------------------------------------------------

    private static void setCurrRegPlot(IqmRegressionPlot pl) {
        currRegPlot = pl;
    }

    private static IqmRegressionPlot getCurrRegPlot() {
        return currRegPlot;
    }
    //----------------------------------------------------------------------------------------------------

    /**
     * this method calculates the linear Regression parameters
     * Y = p[0] + p[1].X
     */
    public static double[] getLinearRegression(Vector<Double> dataX, Vector<Double> dataY, int regStart, int regEnd) {

        LinearRegression lr = new LinearRegression();
        //double p[] = lr.calculateParameters(IqmTools.reverse(dataX), IqmTools.reverse(dataY), regStart, regEnd);
        double[] dataXArray = new double[dataX.size()];
        double[] dataYArray = new double[dataY.size()];

        for (int i = 0; i < dataX.size(); i++) {
            dataXArray[i] = ((Double) dataX.get(i)).doubleValue();
        }
        for (int i = 0; i < dataY.size(); i++) {
            dataYArray[i] = ((Double) dataY.get(i)).doubleValue();
        }
        double p[] = lr.calculateParameters(dataXArray, dataYArray, regStart, regEnd);
        return p;
    }

    //----------------------------------------------------------------------------------------------------
    /**
     * This method gets the maximal possible grey value depending on the image type
     */
    public static double getImgTypeGreyMax(PlanarImage pi) {
        double typeGreyMax = 0d;
        int dt = pi.getSampleModel().getDataType();
        if (dt == DataBuffer.TYPE_BYTE) {
            //typeGreyMax = Byte.MAX_VALUE; //liefert 127!!
            typeGreyMax = 255d;
        }
        if (dt == DataBuffer.TYPE_DOUBLE) {
            typeGreyMax = Double.MAX_VALUE;
        }
        if (dt == DataBuffer.TYPE_FLOAT) {
            typeGreyMax = Float.MAX_VALUE;
        }
        if (dt == DataBuffer.TYPE_INT) {
            typeGreyMax = Integer.MAX_VALUE;
        }
        if (dt == DataBuffer.TYPE_SHORT) {
            typeGreyMax = Short.MAX_VALUE;
        }
        if (dt == DataBuffer.TYPE_USHORT) {
            //typeGreyMax = Short.MAX_VALUE; //????????????????????????????
            typeGreyMax = 65535d; 
        }
        return typeGreyMax;
    }
    //----------------------------------------------------------------------------------------------------

    /**
     * This method reverses a Vector vec
     */
    public static Vector reverseVector(Vector vec) {
        int length = vec.size();
        Vector outVec = new Vector();
        for (int i = 0; i < length; i++) {
            outVec.add(vec.get(length - 1 - i));
        }
        return outVec;
    }
    //----------------------------------------------------------------------------------------------------

    /**
     * This method calculates the center of gravity
     * The center coordinates refer to the upper left corner
     */
    public static double[] getCenterOfGravity(PlanarImage pi) {
        double[] center = new double[2];
        int width = pi.getWidth();
        int height = pi.getHeight();
        int numBands = pi.getNumBands();
        if (numBands == 3) {
            double[][] m = {{1. / 3, 1. / 3, 1. / 3, 0}};
            ParameterBlock pb = new ParameterBlock();
            pb.addSource(pi);
            pb.add(m);
            pi = JAI.create("bandcombine", pb, null);
            pb.removeSources();
            pb.removeParameters();
            pb.addSource(pi);
            pi = JAI.create("invert", pb, null);
        }
        Raster r = pi.getData();
        center[0] = 0.0;  //x
        center[1] = 0.0;  //y
        int sumGrey = 0;
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                center[0] = center[0] + r.getSample(x, y, 0) * x;
                center[1] = center[1] + r.getSample(x, y, 0) * y;
                sumGrey = sumGrey + r.getSample(x, y, 0);
            }
        }
        center[0] = center[0] / sumGrey;
        center[1] = center[1] / sumGrey;
        return center;
    }
    //----------------------------------------------------------------------------------------------------

    /**
     * This method sets the current lookDisplayJAI
     * @param lookDisplayJAI
     */
    public static void setLookDisplayJAI(LookDisplayJAI dj) {
        lookDisplayJAI = dj;
    }

    /**
     * This method gets the current lookDisplayJAI
     * @return lookDisplayJAI
     */
    public static LookDisplayJAI getLookDisplayJAI() {
        return lookDisplayJAI;
    }

    /**
     * This method sets the properties of Iqm in the iqm.ini file
     * @param String propName Property name
     * @param String property
     */
    public static void setIqmIniFile(String propName, String property) {
        String sep = IqmTools.getFileSeparator();
        String userDir = System.getProperty("user.home");
        String iniFileName = userDir + sep + "iqm" + sep + "iqm.ini";
        File iniFile = new File(iniFileName);
        if (iniFile.exists()) {
            Properties props = new Properties();
            //Firstly load all properties
            FileInputStream ins = null;
            try {
                ins = new FileInputStream(iniFileName);
                props.load(ins);
                ins.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //set property
            props.setProperty(propName, property);
            //save properties
            try {
                FileOutputStream fos = new FileOutputStream(iniFileName);
                props.store(fos, "Iqm Properties");
                fos.close();
            } catch (IOException ignored) {
            }
        }

    }

    /**
     * Returns the JAVA representation of the jaitemplates.xml file, for GUI templates.
     * @author Philipp W. <java@scaenicus.net>
     * @return JAIXMLParameterBlocks Representation of the users jaitemplates.xml
     */
    public static JAIXMLParameterBlocks getJaiTemplatesXML() {
        if (jaitemplatesXML == null) {
            jaitemplatesXML = new JAIXMLParameterBlocks();
        }
        return jaitemplatesXML;
    }
    //----------------------------------------------------------------------------------------------------
}//END

